from typing import Dict, List, Optional, Tuple, TypeVar, Union

from graia.application.message.chain import MessageChain
from graia.application.message.elements.internal import Plain
from graia.ptilopsis.signature import FullMatch, NormalMatch, PatternReceiver, Require
from graia.ptilopsis.signature.pack import Arguments, merge_signature_chain
from graia.ptilopsis.utilles import AutoUnpackTuple, InsertGenerator, merge_chain_plain, silce_chain_start

import re
import copy
import itertools

T = Union[NormalMatch, PatternReceiver]
U = Union[T, List[T], Tuple[T]]

MessageIndex = Tuple[int, Optional[int]]

_T = TypeVar("_T")

def origin_or_zero(origin: Optional[_T]) -> Union[_T, int]:
    if origin is None:
        return 0
    return origin

def offset_handle(result: Dict[Arguments, Tuple[
    MessageIndex, MessageIndex
]], offset):
    if offset:
        return {k: (
            (
                (v[0][0] + offset[0]) if all([v[0][0],offset[0]]) else v[0][0],
                (v[0][1] + offset[1]) if all([v[0][1],offset[1]]) else v[0][1]
            ),
            (
                (v[1][0] + offset[0]) if all([v[1][0],offset[0]]) else v[1][0],
                (v[1][1] + offset[1]) if all([v[1][1],offset[1]]) else v[1][1]
            )
        ) for k, v in result.items()}
    return result

class Kanata:
    "彼方."

    def __init__(self) -> None:
        pass

    def detect(self,
        signature_chain: Tuple[U],
        message_chain: MessageChain,
        matching_recevier: Optional[Arguments] = None,
        offset: Optional[MessageIndex] = None
    ) -> Optional[Dict[str, MessageChain]]:
        merged_chain = merge_signature_chain(signature_chain)
        message_chain = merge_chain_plain(message_chain)
    
        reached_message_index: MessageIndex = (0, None)
        # [0] => real_index
        # [1] => text_index(optional)

        start_index: MessageIndex = (0, None)

        match_result: Dict[Arguments, Tuple[
            MessageIndex, # start(include)
            MessageIndex  # stop(exclude)
        ]] = {}
        
        signature_iterable = InsertGenerator(enumerate(merged_chain))
        latest_index = None

        #print(message_chain, merged_chain)
        for signature_index, signature in signature_iterable:
            if isinstance(signature, (Arguments, PatternReceiver)):
                if matching_recevier: # 已经选中了一个...
                    if isinstance(signature, Arguments):
                        if latest_index == signature_index:
                            matching_recevier.content.extend(signature.content)
                            continue
                        else:
                            raise TypeError("a unexpected case: match conflict")
                    if isinstance(signature, PatternReceiver):
                        matching_recevier.content.append(signature)
                        continue
                else:
                    if isinstance(signature, PatternReceiver):
                        signature = Arguments([signature])
                matching_recevier = signature
                start_index = reached_message_index
            elif isinstance(signature, NormalMatch):
                if not matching_recevier:
                    # 如果不要求匹配参数, 从当前位置(reached_message_index)开始匹配FullMatch.
                    current_chain = silce_chain_start(message_chain, reached_message_index)
                    if not current_chain.__root__: # index 越界
                        return
                    if not isinstance(current_chain.__root__[0], Plain):
                        # 切片后第一个**不是** Plain.
                        return
                    re_match_result = re.match(signature.operator(), current_chain.__root__[0].text)
                    if not re_match_result:
                        #print("不匹配!", signature, current_chain.__root__[0].text)
                        # 不匹配的
                        return
                    #print("匹配成功.", signature, current_chain.__root__[0].text)
                    # 推进当前进度.
                    plain_text_length = len(current_chain.__root__[0].text)
                    pattern_length = re_match_result.end() - re_match_result.start()
                    if (pattern_length + 1) >= plain_text_length:
                        # 不推进 text_index 进度, 转而推进 element_index 进度(大概率是真正的全匹配.).
                        reached_message_index = (reached_message_index[0] + 1, None)
                    else:
                        # 推进 element_index 进度至已匹配到的地方后.
                        reached_message_index = (
                            reached_message_index[0],
                            origin_or_zero(reached_message_index[1]) + re_match_result.start() + pattern_length
                        )
                else:
                    # 需要匹配参数(是否贪婪模式查找, 即是否从后向前)
                    greed = matching_recevier.isGreed
                    for element_index, element in \
                        enumerate(silce_chain_start(message_chain, reached_message_index).__root__):
                        if isinstance(element, Plain):
                            current_text: str = element.text
                            # 完成贪婪判断
                            text_find_result_list = list(re.finditer(signature.operator(), current_text))
                            if not text_find_result_list:
                                continue
                            text_find_result = text_find_result_list[-int(greed)]
                            if not text_find_result:
                                continue
                            text_find_index = text_find_result.start()

                            # 找到了! 这里不仅要推进进度, 还要把当前匹配的参数记录结束位置并清理.
                            stop_index = (
                                reached_message_index[0] + element_index + int(element_index == 0),
                                origin_or_zero(reached_message_index[1]) + text_find_index
                            )
                            match_result[matching_recevier] = (copy.copy(start_index), stop_index)
                            print("detected:", reached_message_index, text_find_index, element_index, stop_index)
                            print(f"""{text_find_result=}
                            
                            """)
                            print("detect 成功", matching_recevier, match_result[matching_recevier], signature_index)

                            start_index = (0, None)
                            matching_recevier = None

                            pattern_length = text_find_result.end() - text_find_result.start()
                            if current_text == text_find_result.string[slice(*text_find_result.span())]:
                                # 此处是如果推进 text_index 就会被爆破....
                                # 推进 element_index 而不是 text_index
                                reached_message_index = (reached_message_index[0] + 1, None)
                            else:
                                reached_message_index = (
                                    reached_message_index[0] + element_index,
                                    origin_or_zero(reached_message_index[1]) + text_find_index + pattern_length
                                )
                            break
                    else:
                        # 找遍了都没匹配到.
                        return
            elif isinstance(signature, list):
                #print("并列关系处理前", match_result, "id", id(match_result))
                for sub_signature in signature:
                    if not isinstance(sub_signature, (tuple, list)):
                        sub_signature = [sub_signature]
                    sliced_iter = list(AutoUnpackTuple(merged_chain))[signature_index + 1:]
                    sub_match_signature = list(itertools.chain(sub_signature, sliced_iter))
                    sub_message_chain = silce_chain_start(message_chain, reached_message_index)
                    #print(id(match_result), "开始处理并列关系之一")
                    result = self.detect(
                        sub_match_signature,
                        sub_message_chain, # 因为推进都是推进到后面, 是安全的操作.
                        matching_recevier,
                        reached_message_index
                    )
                    #print("并列处理尝试完毕", sub_signature, id(match_result), match_result, result)
                    if result is not None:
                        print("偏移值:", offset, result)
                        return offset_handle({
                            **match_result,
                            **result
                        }, offset)
                else:
                    # 整个元素都没匹配到.
                    return
            elif isinstance(signature, tuple):
                # 保留当前 signature_index 防止处理并列时发生错误.
                signature_iterable.insert_items.append(
                    ((signature_index, i) for i in merge_signature_chain(signature))
                )
            
            latest_index = signature_index
        else:
            #print("循环结束后打印", match_result)
            if matching_recevier: # 到达了终点, 却仍然还要做点事的.
                # 计算终点坐标.
                text_index = None

                latest_element = message_chain.__root__[-1]
                if isinstance(latest_element, Plain):
                    text_index = len(latest_element.text)

                stop_index = (len(message_chain.__root__), text_index)
                match_result[matching_recevier] = (start_index, stop_index)
        #print("返回结果(循环外)", match_result)
        return offset_handle(match_result, offset)

if __name__ == "__main__":
    from graia.ptilopsis.signature import Require, Optional as TO
    from graia.ptilopsis.utilles import silce_chain_stop
    from devtools import debug
    from graia.application.entry import At
    # 这里是测试代码
    test_message_chain = MessageChain.create([
        Plain("lp player "), At(1846913566), Plain("12 info")
    ])
    test_signature_chain = (
        [FullMatch("lp"), FullMatch("luckperms")], FullMatch(" "),
        [
            (
                [FullMatch("g"), FullMatch("group")], FullMatch(" "),
                Require(name="group"), FullMatch(" "),
                FullMatch("info")
            ),
            (
                [FullMatch("p"), FullMatch("player")], FullMatch(" "),
                Require(name="player"), FullMatch(" "),
                FullMatch("info"),
            )
        ]
    )
    test_station = Kanata()
    match_result = test_station.detect(test_signature_chain, test_message_chain)
    debug(match_result)
    if match_result:
        debug({k: silce_chain_stop(
            silce_chain_start(test_message_chain, v[0]),
                (v[1][0], v[1][1] - origin_or_zero(v[0][1]))
        ) for k, v in match_result.items()})
